#pragma once
#include <iostream>
#include <semaphore.h>
#include <unistd.h>
#include <vector>
#include <cassert>
#include <pthread.h>
static const int gcap = 5;
template <class T>
class RingQueue
{
private:
    void P(sem_t &spaceSem)
    {
        int n = sem_wait(&spaceSem);
        assert(0 == n); // 这里最好if
        (void)n;
    }
    void V(sem_t &dataSem)
    {
        int n = sem_post(&dataSem);
        assert(0 == n); // 这里最好if
        (void)n;
    }

public:
    RingQueue(const int &cap = gcap)
        : _queue(cap) // 可以这样初始化vector
        , _cap(cap), _productorStep(0), _consumerStep(0)
    {
        // int sem_init(sem_t *sem, int pshared, unsigned int value);
        // pshared  0线程共享 非0进程共享（开辟在共享内存）
        // value 初始值
        int n = sem_init(&_spaceSem, 0, _cap);
        assert(0 == n);
        n = sem_init(&_dataSem, 0, 0);
        assert(0 == n);

        pthread_mutex_init(&_pmutex, nullptr);
        pthread_mutex_init(&_cmutex, nullptr);
    }
    // 生产者调用Push
    void Push(const T &in) // 向环形队列中push数据
    {
        P(_spaceSem); // 调用sem_wait，信号量申请成功，未来一定能访问到临界资源

        pthread_mutex_lock(&_pmutex);
        _queue[_productorStep++] = in;
        _productorStep %= _cap;

        pthread_mutex_unlock(&_pmutex);
        V(_dataSem); // 生产完毕后，调用V操作，让消费者信号量++。
    }
    // 消费者调用Pop
    void Pop(T *out) // 向环形队列中pop数据,out是输出型参数
    {
        P(_dataSem); // 确认消费者信号量是否可以--，不能减就阻塞

        pthread_mutex_lock(&_cmutex);
        *out = _queue[_consumerStep++];
        _consumerStep %= _cap;

        pthread_mutex_unlock(&_cmutex);
        V(_spaceSem); // 消费完毕后，生产者信号量++
    }
    ~RingQueue()
    {
        sem_destroy(&_spaceSem);
        sem_destroy(&_dataSem);

        pthread_mutex_destroy(&_pmutex);
        pthread_mutex_destroy(&_cmutex);
    }

private:
    std::vector<T> _queue;   // vector模拟队列
    int _cap;                // 队列的最大容量
    sem_t _spaceSem;         // 生产者信号量，表明环形队列中剩余空间资源数量
    sem_t _dataSem;          // 消费者信号量，表明环形队列中存在的数据资源数量
    int _productorStep;      // 生产者在循环列表的脚步
    int _consumerStep;       // 消费者在循环列表的脚步
    pthread_mutex_t _pmutex; // 生产者锁
    pthread_mutex_t _cmutex; // 消费者锁
};